//
//  FocusTask.swift
//  CatPomodoroTimer
//
//  Created by Xuri Li on 2024/8/28.
//

import Foundation
import SQLite

struct FocusTask: Codable {
    enum TaskStatus: String, Codable {
        case incomplete
        case complete
    }
    
    var id: Int64?
    var creationDate: Date
    var endDate: Date?
    var status: TaskStatus
    var focusDurationLevel: Profile.FocusDurationLevel
    
    init(id: Int64? = nil, creationDate: Date = Date(), endDate: Date? = nil, status: TaskStatus = .incomplete, focusDurationLevel: Profile.FocusDurationLevel) {
        self.id = id
        self.creationDate = creationDate
        self.endDate = endDate
        self.status = status
        self.focusDurationLevel = focusDurationLevel
    }
    
    private enum CodingKeys: String, CodingKey {
        case id, creationDate, endDate, status, focusDurationLevel
    }
    
    // SQLite table and columns
    static let table = Table("FocusTasks")
    static let id = Expression<Int64>("id")
    static let creationDate = Expression<Date>("creationDate")
    static let endDate = Expression<Date?>("endDate")
    static let status = Expression<String>("status")
    static let focusDurationLevel = Expression<Int>("focusDurationLevel")
    
    // MARK: Base Functions
    
    // CRUD operations
    static func createTable(db: Connection) throws {
        try db.run(table.create { t in
            t.column(id, primaryKey: .autoincrement)
            t.column(creationDate)
            t.column(endDate)
            t.column(status)
            t.column(focusDurationLevel)
        })
    }
    
    // Insert - Instance method
    func insert(db: Connection) throws -> Int64 {
        let insert = FocusTask.table.insert(
            FocusTask.creationDate <- self.creationDate,
            FocusTask.endDate <- self.endDate,
            FocusTask.status <- self.status.rawValue,
            FocusTask.focusDurationLevel <- self.focusDurationLevel.rawValue
        )
        return try db.run(insert)
    }
    
    // Insert - static method
    @discardableResult
    static func insert(db: Connection, task: FocusTask) throws -> FocusTask {
        let insert = table.insert(
            FocusTask.creationDate <- task.creationDate,
            FocusTask.endDate <- task.endDate,
            FocusTask.status <- task.status.rawValue,
            FocusTask.focusDurationLevel <- task.focusDurationLevel.rawValue
        )
        
        // 执行插入操作并获取插入的行ID
        let id = try db.run(insert)
        
        // 返回带有生成ID的新FocusTask对象
        return FocusTask(
            id: id,
            creationDate: task.creationDate,
            endDate: task.endDate,
            status: task.status,
            focusDurationLevel: task.focusDurationLevel
        )
    }
    
    static func fetchAll(db: Connection) throws -> [FocusTask] {
        let query = table.select(*)
        var tasks: [FocusTask] = []
        for row in try db.prepare(query) {
            let task = FocusTask(
                creationDate: row[creationDate],
                endDate: row[endDate],
                status: TaskStatus(rawValue: row[status]) ?? .incomplete,
                focusDurationLevel: Profile.FocusDurationLevel(rawValue: row[focusDurationLevel]) ?? .level3
            )
            tasks.append(task)
        }
        return tasks
    }
    
    static func update(db: Connection, task: FocusTask) throws {
        // Check if the task has a valid id
        guard let taskId = task.id else {
            throw NSError(domain: "FocusTaskError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Task ID is nil. Update operation requires a valid task ID."])
        }
        
        // Create a query to find the task by its ID
        let taskToUpdate = table.filter(id == taskId)
        
        // Run the update
        try db.run(taskToUpdate.update(
            creationDate <- task.creationDate,
            endDate <- task.endDate,
            status <- task.status.rawValue,
            focusDurationLevel <- task.focusDurationLevel.rawValue
        ))
    }
    
    static func delete(db: Connection, taskId: Int64) throws {
        let task = table.filter(id == taskId)
        try db.run(task.delete())
    }
    
    // MARK: Extension Functions
    // Fetch tasks for today
    static func fetchTasksForToday(db: Connection, completedOnly: Bool = false) throws -> [FocusTask] {
        let (todayStart, todayEnd) = getTodayStartAndEnd()
        
        // Apply date filter and optionally filter by completion status
        let dateFilter = creationDate >= todayStart && creationDate < todayEnd
        let query = completedOnly ? table.filter(dateFilter && status == TaskStatus.complete.rawValue) : table.filter(dateFilter)
                
        var tasks: [FocusTask] = []
        for row in try db.prepare(query) {
            let task = FocusTask(
                creationDate: row[creationDate],
                endDate: row[endDate],
                status: TaskStatus(rawValue: row[status]) ?? .incomplete,
                focusDurationLevel: Profile.FocusDurationLevel(rawValue: row[focusDurationLevel]) ?? .level3
            )
            tasks.append(task)
        }
        
        return tasks
    }
    
    // MARK: Helper Functions
    // Get Today's Start and End
    private static func getTodayStartAndEnd() -> (start: Date, end: Date) {
        let calendar = Calendar.current
        let now = Date()
        
        // Get the start of today
        let startOfDay = calendar.startOfDay(for: now)
        
        // Get the start of tomorrow
        guard let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay) else {
            fatalError("Unable to calculate end of today")
        }
        
        return (start: startOfDay, end: endOfDay)
    }
}




